﻿namespace FluentMvc.MvcIntegration
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Fasterflect;

    public class FluentModelPropertyValidator<TModel> : ModelValidator where TModel : class,new()
    {
        private readonly ModelContext<TModel> modelContext;
        private readonly TModel containerObject;

        public FluentModelPropertyValidator(ModelMetadata metadata, ControllerContext controllerContext) : base(metadata, controllerContext)
        {
            modelContext = new ModelContext<TModel>();
            containerObject = (TModel) metadata.ContainerType.CreateInstance();
            ((IModelInitializer<TModel>)containerObject).InitializeModel(modelContext);
        }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            var modelValidator = new ModelValidator<TModel>(modelContext);
            return modelValidator.ValidateProperty(Metadata.PropertyName, Metadata.Model, (TModel) container);
        }

        public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        {
            var setting = modelContext.GetSettings(Metadata.PropertyName);
            if (setting == null)
                return Enumerable.Empty<ModelClientValidationRule>();
            var rules = new List<ModelClientValidationRule>();
            foreach (var remoteValidator in setting.RemoteValidators)
            {
                var remoteValidationRule = new ModelClientValidationRule
                                                    {
                                                        ValidationType = "remote",                                  
                                                    };
                var additionalFields = new List<string>(remoteValidator.AdditionalFields);
                additionalFields.Add(remoteValidator.PropertyName);
                remoteValidationRule.ValidationParameters.Add("additionalfields", string.Join(",", additionalFields.Select(field => "*." + field)));
                var url = new UrlHelper(new RequestContext(ControllerContext.RequestContext.HttpContext, new RouteData())).Action(remoteValidator.Action, remoteValidator.Controller, new {area = remoteValidator.Area});
                remoteValidationRule.ValidationParameters.Add("url", url);
                rules.Add(remoteValidationRule);
            }
            foreach (var rule in setting.ValidationSettings)
            {
                if (rule.When != null && !rule.When(containerObject))
                    continue;
                if (rule.Ignore != null && rule.Ignore(containerObject))
                    continue;
                var message = rule.GetMessage != null ? rule.GetMessage() : rule.Message;
                if (rule.ClientValidationRules != null)
                    rules.AddRange(rule.ClientValidationRules);
                if (rule.Rule != null)
                    rule.Rule.ErrorMessage = message;
                if (rule.Rule != null && typeof(IClientValidatable).IsAssignableFrom(rule.Rule.GetType()))
                    rules.AddRange(((IClientValidatable)rule.Rule).GetClientValidationRules(Metadata, ControllerContext));
                foreach (var validationRule in rules)
                {
                    validationRule.ErrorMessage = validationRule.ErrorMessage ?? message;
                    if (validationRule.ValidationParameters == null)
                        continue;
                }
            }
            return rules;
        }
    }
}